// 守护进程接口
//这段代码定义了一个 daemonize 函数，
//用于将当前进程转变为守护进程（Daemon）。
//守护进程是一种在后台运行的特殊进程，通常用于执行系统服务。

#include <iostream>
#include <cstdio>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

void daemonize() {
    int fd = 0;

    // 1. 忽略SIGPIPE
    //当进程尝试写入一个已经关闭或不可写的管道或套接字时，操作系统会发送 SIGPIPE 信号，
    //默认情况下会导致进程终止。通过忽略这个信号，可以让程序在遇到这种情况时继续运行而不是崩溃。
    signal(SIGPIPE, SIG_IGN);
    // 2. 改变工作路径
    //chdir(const char *__path);
    // 3. 不要成为进程组组长
    //确保子进程不是进程组组长，以便后续调用 setsid() 成功
    //fork() 创建一个新的子进程。父进程会得到子进程的 PID 并立即退出（exit(0)）
    //这样就保证了只有子进程继续运行，并且它不会是进程组组长。
    if (fork() > 0) {
        exit(0);
    }
    // 4. 创建独立会话
    //创建一个新的会话，使该进程成为新会话的会话首进程、新进程组的组长，并且脱离控制终端。
    //setsid() 调用后，进程不再与任何终端相关联，成为一个独立的会话和进程组的领导者。
    setsid();
    // 重定向文件描述符0 1 2
    //将标准输入（0）、标准输出（1）和标准错误（2）重定向到 /dev/null，以防止它们占用不必要的资源。
    //最后检查 fd 是否大于 STDERR_FILENO（即 2），如果是，则关闭多余的文件描述符 fd，因为它已经被复制到标准输入、输出和错误中了。
    if ((fd = open("/dev/null", O_RDWR)) != -1) { // 执行成功fd大概率为3
        dup2(fd, STDIN_FILENO);
        dup2(fd, STDOUT_FILENO);
        dup2(fd, STDERR_FILENO);

        // dup2三个标准流之后, fd就没有用了
        //原本打开 /dev/null 得到的文件描述符 fd 已经没有用了，因为它已经被复制到了标准输入、输出和错误的文件描述符上。（上面的重定向）
        if (fd > STDERR_FILENO) {
            close(fd);
        }
    }
}
